#!/usr/bin/env bash

YAML_DEBUG=false

include "output.sh"
include "test.sh"
include "regex.sh"
include "python.sh"

_YAML_PYTHON_HEADER="exit(1)"
function yaml::open()
{
    if ! [ -f "$1" ]; then
        loge "yaml::open 需要文件"
        return 1
    fi

    local code="
        import yaml
        with open('$1', 'r') as f:
            data = yaml.load(f.read(), Loader=yaml.FullLoader)
    "
    Python::ExecNoResponse default "$(print_source 8 code)"
}

function yaml::_get_keys()
{
    local key=""
    local i
    for (( i = 1; i <= $#; i++ )); do
        if [[ "${!i}" =~ (^$regex_dec_integer$) ]]; then
            # 数字
            key+="[${!i}]"
        else
            # 字符串
            key+="['${!i}']"
        fi
    done
    echo "$key"
}

function yaml::is_exists()
{
    local keys="$(yaml::_get_keys $*)"
    local code="
        try:
            response(data$keys == data$keys)
        except:
            response(False)
    "
    [ "$(Python::Exec default "$(print_source 8 code)")" == "True" ]
}

function yaml::_instance_of()
{
    local type="$1"
    shift

    if $YAML_DEBUG && ! yaml::is_exists $*; then
        assert_fail "元素$*不存在" || return
    fi

    local code="
        response(type(data$(yaml::_get_keys $*)) == $type)
    "
    [ "$(Python::Exec default "$(print_source 8 code)")" == "True" ]
}

function yaml::is_array()
{
    yaml::_instance_of list $*
}

function yaml::is_object()
{
    yaml::_instance_of dict $*
}

function yaml::is_string()
{
    yaml::_instance_of str $*
}

function yaml::is_bool()
{
    yaml::_instance_of bool $*
}

function yaml::is_integer()
{
    yaml::_instance_of int $*
}

function yaml::is_float()
{
    yaml::_instance_of float $*
}

function yaml::is_null()
{
    local code="
        response(data$(yaml::_get_keys $*) == None)
    "
    [ "$(Python::Exec default "$(print_source 8 code)")" == "True" ]
}

# 获取yaml对象的所有键, 可以用 for in 来遍历
# for key in $(yaml::get_keys); do echo $key; done
function yaml::get_keys()
{
    if $YAML_DEBUG && ! yaml::is_object $*; then
        assert_fail "元素$*不是对象" || return
    fi

    local code="
        response(' '.join(data$(yaml::_get_keys $*).keys()))
    "
    Python::Exec default "$(print_source 8 code)"
}

function yaml::get_val()
{
    local code="
        response(data$(yaml::_get_keys $*))
    "
    Python::Exec default "$(print_source 8 code)"
}

function yaml::len()
{
    local code="
        response(len(data$(yaml::_get_keys $*)))
    "
    Python::Exec default "$(print_source 8 code)"
}

function yaml::test::prepare_test_text()
{
    local yaml_source="
        repo_dir: \$HOME/repo/srepos
        repos:
          https://gitee.com/karl1864:
            private_data:
              action:
                - ln ssh/config \$HOME/.ssh/config
            config_crx:
              action:
                - ln . \$HOME/crx
                - 123
                - 1.23
                - 1e9
                - 1230000000.0
                - 077
                - 0xabc
                - 0b1010_0111_0100_1010_1110
                - TRUE
                - FALSE
                - ~
                - a
                  b
                  c
                -
                  a: 1
                  b: 2
                  c: ~
    "
    cat <<< "$(print_source 8 yaml_source)" > "/tmp/1.yml"
}

function yaml::test1()
{
    rm -f /tmp/1.yml
    test::should_return_false yaml::open "/tmp/1.yml"

    yaml::test::prepare_test_text
    test::should_return_true yaml::open "/tmp/1.yml"

    test::should_return_true yaml::is_exists repo_dir

    test::run_cmd yaml::get_val repo_dir
    test::should_be "\$HOME/repo/srepos"

    test::should_return_false yaml::is_exists repo
    test::should_return_true  yaml::is_exists repos
    test::should_return_true  yaml::is_object repos
    test::should_return_true  yaml::is_exists repos https://gitee.com/karl1864
    test::should_return_true  yaml::is_object repos https://gitee.com/karl1864
    test::should_return_true  yaml::is_exists repos https://gitee.com/karl1864 private_data
    test::should_return_true  yaml::is_object repos https://gitee.com/karl1864 private_data
    test::should_return_true  yaml::is_exists repos https://gitee.com/karl1864 private_data action
    test::should_return_true  yaml::is_array  repos https://gitee.com/karl1864 private_data action

    local keys=(repos https://gitee.com/karl1864 private_data action)
    test::run_cmd yaml::len ${keys[*]}
    test::should_be 1

    test::run_cmd yaml::get_val ${keys[*]} 0
    test::should_be "ln ssh/config \$HOME/.ssh/config"

    keys=(repos https://gitee.com/karl1864 config_crx action)
    test::run_cmd yaml::len ${keys[*]}
    test::should_be 13

    test::should_return_true yaml::is_string ${keys[*]} 0
    test::run_cmd yaml::get_val ${keys[*]} 0
    test::should_be "ln . \$HOME/crx"

    test::should_return_true yaml::is_integer ${keys[*]} 1
    test::run_cmd yaml::get_val ${keys[*]} 1
    test::should_be "123"

    test::should_return_true yaml::is_float ${keys[*]} 2
    test::run_cmd yaml::get_val ${keys[*]} 2
    test::should_be "1.23"

    test::should_return_true yaml::is_string ${keys[*]} 3
    test::run_cmd yaml::get_val ${keys[*]} 3
    test::should_be "1e9"

    test::should_return_true yaml::is_float ${keys[*]} 4
    test::run_cmd yaml::get_val ${keys[*]} 4
    test::should_be "1230000000.0"

    test::should_return_true yaml::is_integer ${keys[*]} 5
    test::run_cmd yaml::get_val ${keys[*]} 5
    test::should_be "$((077))"

    test::should_return_true yaml::is_integer ${keys[*]} 6
    test::run_cmd yaml::get_val ${keys[*]} 6
    test::should_be "$((0xabc))"

    test::should_return_true yaml::is_integer ${keys[*]} 7
    test::run_cmd yaml::get_val ${keys[*]} 7
    test::should_be "$((2#10100111010010101110))"

    test::should_return_true yaml::is_bool ${keys[*]} 8
    test::run_cmd yaml::get_val ${keys[*]} 8
    test::should_be "True"

    test::should_return_true yaml::is_bool ${keys[*]} 9
    test::run_cmd yaml::get_val ${keys[*]} 9
    test::should_be "False"

    test::should_return_true yaml::is_null ${keys[*]} 10
    test::run_cmd yaml::get_val ${keys[*]} 10
    test::should_be "None"

    test::should_return_true yaml::is_string ${keys[*]} 11
    test::run_cmd yaml::get_val ${keys[*]} 11
    test::should_be "a b c"

    test::should_return_true yaml::is_object ${keys[*]} 12
    test::run_cmd yaml::get_val ${keys[*]} 12
    test::should_be "{'a': 1, 'b': 2, 'c': None}"

    test::should_return_true yaml::is_integer ${keys[*]} 12 a
    test::run_cmd yaml::get_val ${keys[*]} 12 a
    test::should_be "1"

    test::should_return_true yaml::is_integer ${keys[*]} 12 b
    test::run_cmd yaml::get_val ${keys[*]} 12 b
    test::should_be "2"

    test::should_return_true yaml::is_null ${keys[*]} 12 c
    test::run_cmd yaml::get_val ${keys[*]} 12 c
    test::should_be "None"

    test::run_cmd yaml::get_keys ${keys[*]} 12
    test::should_be "a b c"

}
test::register_test yaml::test1
